#ifndef __M_BIND_H__
#define __M_BIND_H__
#include "../mqcommon/mq_logger.hpp"
#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <iostream>
#include <unordered_map>
#include <mutex>
#include <memory>

namespace castlemq
{
    struct Binding
    {
        using ptr=std::shared_ptr<Binding>;
        std::string exchange_name;
        std::string msg_queue_name;
        std::string binding_key;

        Binding(){}
        Binding(const std::string& ename,const std::string& qname,const std::string& key)
        :exchange_name(ename),msg_queue_name(qname),binding_key(key){}
    };

    //队列名称与Binding一一对应,方便通过队列名查找绑定关系
    using MsgQueueBindingMap = std::unordered_map<std::string,Binding::ptr>;
    //交换机对应着多个队列，以交换机为单元
    using BindingMap = std::unordered_map<std::string,MsgQueueBindingMap>;
    class BindingMapper
    {
        public:
            BindingMapper(const std::string& dbfile):_sql_helper(dbfile)
            {
                std::string path = fileHelper::parentDictory(dbfile);
                fileHelper::createDictory(path);
                assert(_sql_helper.open());
                createTable();
            }
            void createTable()
            {
                #define CREAT_BINDING_TABLE "create table if not exists binding_table(exchange_name varchar(32),msg_queue_name varchar(32),binding_key varchar(128));"
                char sql_str[4096] = {0};
                sprintf(sql_str,CREAT_BINDING_TABLE);
                bool ret=_sql_helper.exec(sql_str,nullptr,nullptr);
                if(!ret)
                {
                    DLOG("创建binding表失败！");
                    abort();//结束进程
                }
            }
            void removeTable()
            {
                #define DROP_BINGDING_TABLE "drop table if exists binding_table;"
                char sql_str[4096] = {0};
                sprintf(sql_str,DROP_BINGDING_TABLE);
                bool ret=_sql_helper.exec(sql_str,nullptr,nullptr);
                if(!ret)
                {
                    DLOG("删除队列表失败！");
                }
            }
            bool insert(Binding::ptr& binding)
            {
                #define INSERT_BINDING_TABLE "insert into binding_table values('%s','%s','%s');"
                char sql_str[4096] = {0};
                sprintf(sql_str,INSERT_BINDING_TABLE,(binding->exchange_name).c_str(),(binding->msg_queue_name).c_str(),(binding->binding_key).c_str());
                return _sql_helper.exec(sql_str,nullptr,nullptr);
            }
            void remove(const std::string& ename,const std::string& qname)
            {
                #define REMOVE_TABLE_WHIT_EX_QU "delete from binding_table where exchange_name='%s' and msg_queue_name='%s';"
                char sql_str[4096] = {0};
                sprintf(sql_str,REMOVE_TABLE_WHIT_EX_QU,ename.c_str(),qname.c_str());
                _sql_helper.exec(sql_str,nullptr,nullptr);
            }
            void removeExchangeBindings(const std::string& ename)
            {
                #define REMOVE_TABLE_WHIT_EXCHANGE "delete from binding_table where exchange_name='%s';"
                char sql_str[4096] = {0};
                sprintf(sql_str,REMOVE_TABLE_WHIT_EXCHANGE,ename.c_str());
                _sql_helper.exec(sql_str,nullptr,nullptr);
            }
            void removeMsgQueueBindings(const std::string& qname)
            {
                #define REMOVE_TABLE_WHIT_QUEUE "delete from binding_table where msg_queue_name='%s';"
                char sql_str[4096] = {0};
                sprintf(sql_str,REMOVE_TABLE_WHIT_QUEUE,qname.c_str());
                _sql_helper.exec(sql_str,nullptr,nullptr);
            }
            BindingMap recovery()
            {
                BindingMap result;
                std::string ss="select exchange_name,msg_queue_name,binding_key from binding_table;";
                _sql_helper.exec(ss.c_str(),SqliteCallback,&result);
                return result;
            }
        private:
            static int SqliteCallback(void* arg,int numcol,char** row,char** fields)
            {
                BindingMap* result = (BindingMap*)arg;
                auto bdp=std::make_shared<Binding>();
                bdp->exchange_name=row[0];
                bdp->msg_queue_name=row[1];
                bdp->binding_key=row[2];

                //为了防止交换机相关绑定数据已经存在，不能直接创建队列映射进行添加，会覆盖历史数据
                //因此先获得交换机映射对象，往里边添加数据
                //但是如果此时没有交换机对应的映射信息，因此这里需要使用引用（保证不存在直接创建）
                MsgQueueBindingMap& qmap=(*result)[bdp->exchange_name];
                qmap.insert(std::make_pair(bdp->msg_queue_name,bdp));
                return 0;
            }
        private:
            SqliteHelper _sql_helper;
    };

    class BindingManager
    {
        public:
            using ptr=std::shared_ptr<BindingManager>;
            BindingManager(const std::string& dbfile):_mapper(dbfile)
            {
                _bindings=_mapper.recovery();
            }
            bool bind(const std::string& ename,const std::string& qname,const std::string& key,bool durable)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it=_bindings.find(ename);
                if(it!=_bindings.end()&&it->second.find(qname)!=it->second.end())   return true;
                Binding::ptr bp=std::make_shared<Binding>(ename,qname,key);
                if(durable)
                {
                    bool ret=_mapper.insert(bp);
                    if(!ret)    return false;
                }
                auto& qbmap=_bindings[ename];
                qbmap.insert(std::make_pair(qname,bp));
                return true;     
            }
            void unbind(const std::string& ename,const std::string& qname)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto eit=_bindings.find(ename);
                if(eit==_bindings.end())   return;
                auto qit=eit->second.find(qname);
                if(qit==eit->second.end())  return;
                _bindings[ename].erase(qname);

                _mapper.remove(ename,qname);
            }
            void removeExchangeBindings(const std::string& ename)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _mapper.removeExchangeBindings(ename);
                _bindings.erase(ename);
            }
            void removeMsgQueueBindings(const std::string& qname)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _mapper.removeMsgQueueBindings(qname);
                for(auto& x : _bindings)
                {
                    if(x.second.find(qname)!=x.second.end())
                        x.second.erase(qname);
                }
            }
            MsgQueueBindingMap getExchangeBindings(const std::string& ename)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto eit=_bindings.find(ename);
                if(eit==_bindings.end())   
                    return MsgQueueBindingMap();
                return eit->second;
            }
            Binding::ptr getBinding(const std::string& ename,const std::string& qname)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto eit=_bindings.find(ename);
                if(eit==_bindings.end())   
                    return Binding::ptr();
                auto qit=eit->second.find(qname);
                if(qit==eit->second.end())  
                    return Binding::ptr(); 
                return qit->second;
            }
            bool exists(const std::string& ename,const std::string& qname)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto eit=_bindings.find(ename);
                if(eit==_bindings.end())   
                    return false;
                auto qit=eit->second.find(qname);
                if(qit==eit->second.end())  
                    return false; 
                return true;
            }
            size_t size()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                size_t total_size=0;
                for(auto& x : _bindings)
                {
                    total_size+=x.second.size();
                }
                return total_size;
            }
            void clear()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _mapper.removeTable();
                _bindings.clear();
            }
        private:
            std::mutex _mutex;
            BindingMapper _mapper;
            BindingMap _bindings;
    };
}
#endif